﻿using Devonline.Communication.Messages;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;

namespace Devonline.Identity.Admin.Controllers;

/// <summary>
/// 身份资源管理
/// </summary>
/// <returns></returns>
[Route("api/[controller]")]
[ApiController]
[SecurityHeaders]
[Authorize]
public class UserProfileController : ControllerBase
{
    private readonly UserStore _userStore;
    private readonly UserManager<User> _userManager;
    private readonly AuthorizationService _authorizationService;
    private readonly IDataWithAttachmentService<User> _dataService;
    private readonly ILogger<UserProfileController> _logger;
    private readonly IMessageCommunicator _communicator;
    private static CancellationToken CancellationToken => CancellationToken.None;

    public UserProfileController(
        UserStore userStore,
        UserManager<User> userManager,
        AuthorizationService authorizationService,
        IDataWithAttachmentService<User> dataService,
        IMessageCommunicator communicator,
        ILogger<UserProfileController> logger
        )
    {
        _userStore = userStore;
        _userManager = userManager;
        _authorizationService = authorizationService;
        _dataService = dataService;
        _logger = logger;
        _communicator = communicator;
        _userManager.Logger = _logger;
    }

    #region 获取用户信息
    /// <summary>
    /// get user for filter
    /// </summary>
    /// <returns></returns>
    [HttpGet]
    public async Task<IActionResult> GetAsync()
    {
        _logger.LogInformation("user {user} query his info", _dataService.UserName);
        var user = await _userManager.FindByIdAsync(_dataService.UserId);
        if (user is not null)
        {
            return Ok(user.CopyTo<UserViewModel>());
        }

        return BadRequest("您的个人信息不存在!");
    }

    [HttpGet("GetChangePhoneNumberToken")]
    public async Task<IActionResult> GetChangePhoneNumberTokenAsync()
    {
        _logger.LogInformation("user {user} will change phoneNumber", _dataService.UserName);
        var user = await _userManager.FindByIdAsync(_dataService.UserId);
        if (user is not null)
        {
            if (string.IsNullOrWhiteSpace(user.PhoneNumber))
            {
                return BadRequest("你的手机号码不存在!");
            }

            var token = await _userManager.GenerateChangePhoneNumberTokenAsync(user, user.PhoneNumber);
            if (token is not null)
            {
                return Ok(token);
            }
        }

        return BadRequest("您的个人信息不存在!");
    }
    [HttpGet("GetChangeEmailToken")]
    public async Task<IActionResult> GetChangeEmailTokenAsync()
    {
        _logger.LogInformation("user {user} will change email", _dataService.UserName);
        var user = await _userManager.FindByIdAsync(_dataService.UserId);
        if (user is not null)
        {
            if (string.IsNullOrWhiteSpace(user.Email))
            {
                return BadRequest("你的电子邮件不存在!");
            }

            var token = await _userManager.GenerateChangeEmailTokenAsync(user, user.Email);
            if (token is not null)
            {
                return Ok(token);
            }
        }

        return BadRequest("您的个人信息不存在!");
    }
    #endregion

    #region 设置用户基本信息
    /// <summary>
    /// 设置用户基本信息
    /// </summary>
    /// <param name="viewModel"></param>
    /// <returns></returns>
    [HttpPost]
    public async Task<IActionResult> PostAsync(UserIdentityViewModel viewModel)
    {
        var user = await _userManager.FindByIdAsync(_dataService.UserId);
        if (user is null)
        {
            return BadRequest($"用户 {viewModel.Name} 不存在!");
        }

        if (viewModel.Name is not null && viewModel.Name != user.Name)
        {
            _logger.LogInformation("user {user} will set name to {name}", _dataService.UserName, viewModel.Name);
            await _userStore.SetNameAsync(user, viewModel.Name, CancellationToken);
        }

        if (viewModel.Alias is not null && viewModel.Alias != user.Alias)
        {
            _logger.LogInformation("user {user} will set alias to {alias}", _dataService.UserName, viewModel.Alias);
            await _userStore.SetAliasAsync(user, viewModel.Alias, CancellationToken);
        }

        if (viewModel.Image is not null && viewModel.Image != user.Image)
        {
            if (viewModel.Attachments is null || viewModel.Attachments.Count == 0)
            {
                return BadRequest($"用户 {user.Name} 未上传头像信息!");
            }

            _logger.LogInformation("user {user} will set image to {image}", _dataService.UserName, viewModel.Image);
            await _dataService.UpdateAttachmentsAsync(user, viewModel.Attachments, "User.Image");
            await _userStore.SetImageAsync(user, viewModel.Image, CancellationToken);
        }

        if (viewModel.PhoneNumber != user.PhoneNumber)
        {
            _logger.LogInformation("user {user} will set phoneNumber to {phoneNumber}", _dataService.UserName, viewModel.PhoneNumber);
            user.PhoneNumber = viewModel.PhoneNumber;
        }

        if (viewModel.Email != user.Email)
        {
            _logger.LogInformation("user {user} will set email to {email}", _dataService.UserName, viewModel.Email);
            user.Email = viewModel.Email;
        }

        if (viewModel.Description is not null && viewModel.Description != user.Description)
        {
            user.Description = viewModel.Description;
        }

        //user.Update(_dataService.UserName);
        var identityResult = await _userStore.UpdateAsync(user);
        if (identityResult.Succeeded)
        {
            await _dataService.SaveChangesAsync();
            _logger.LogInformation("user {user} success to set the user profile!", _dataService.UserName);
            await RefreshUserInfo(user);
            return Ok();
        }

        var errorMessage = identityResult.GetErrorMessage();
        _logger.LogWarning("user {user} fail to change the profile, error message is: {errorMessage}", _dataService.UserName, errorMessage);
        return BadRequest(errorMessage);
    }
    #endregion

    #region 修改用户敏感信息
    [HttpPost("ChangePassword")]
    public async Task<IActionResult> ChangePasswordAsync(ChangePasswordViewModel viewModel)
    {
        if (string.IsNullOrWhiteSpace(viewModel.UserName) || string.IsNullOrWhiteSpace(viewModel.Password) || string.IsNullOrWhiteSpace(viewModel.NewPassword))
        {
            return BadRequest($"用户名, 密码和新密码都不可为空!");
        }

        _logger.LogInformation("user {user} will change the password", _dataService.UserName);
        var user = await _userManager.FindByNameAsync(_dataService.UserName);
        if (user is null)
        {
            return BadRequest($"用户 {_dataService.UserName} 不存在!");
        }

        var result = await _userManager.ChangePasswordAsync(user, viewModel.Password, viewModel.NewPassword);
        if (result.Succeeded)
        {
            _logger.LogWarning("user {user} success to change the password", _dataService.UserName);
            await RefreshUserInfo(user);
            return Ok();
        }

        var errorMessage = result.GetErrorMessage();
        _logger.LogWarning("user {user} fail to change the password, error message is: {errorMessage}", _dataService.UserName, errorMessage);
        return BadRequest(errorMessage);
    }
    [HttpPost("ChangePhoneNumber")]
    public async Task<IActionResult> ChangePhoneNumberAsync(ChangePhoneNumberViewModel viewModel)
    {
        if (string.IsNullOrWhiteSpace(viewModel.UserName) || string.IsNullOrWhiteSpace(viewModel.PhoneNumber) || string.IsNullOrWhiteSpace(viewModel.Token))
        {
            return BadRequest($"用户名和手机号码都不可为空!");
        }

        _logger.LogInformation("user {user} will change the phone number", _dataService.UserName);
        var user = await _userManager.FindByIdAsync(_dataService.UserId);
        if (user is null)
        {
            return BadRequest($"用户 {_dataService.UserName} 不存在!");
        }

        var result = await _userManager.ChangePhoneNumberAsync(user, viewModel.PhoneNumber, viewModel.Token);
        if (result.Succeeded)
        {
            _logger.LogWarning("user {user} success to change the phone number", _dataService.UserName);
            await RefreshUserInfo(user);
            return Ok();
        }

        var errorMessage = result.GetErrorMessage();
        _logger.LogWarning("user {user} fail to change the phone number, error message is: {errorMessage}", _dataService.UserName, errorMessage);
        return BadRequest(errorMessage);
    }
    [HttpPost("ChangeEmail")]
    public async Task<IActionResult> ChangeEmailAsync(ChangeEmailViewModel viewModel)
    {
        if (string.IsNullOrWhiteSpace(viewModel.UserName) || string.IsNullOrWhiteSpace(viewModel.Email) || string.IsNullOrWhiteSpace(viewModel.Token))
        {
            return BadRequest($"用户名和电子邮件地址都不可为空!");
        }

        _logger.LogInformation("user {user} will change the email", _dataService.UserName);
        var user = await _userManager.FindByIdAsync(_dataService.UserId);
        if (user is null)
        {
            return BadRequest($"用户 {_dataService.UserName} 不存在!");
        }

        var result = await _userManager.ChangeEmailAsync(user, viewModel.Email, viewModel.Token);
        if (result.Succeeded)
        {
            _logger.LogWarning("user {user} success to change the email", _dataService.UserName);
            await RefreshUserInfo(user);
            return Ok();
        }

        var errorMessage = result.GetErrorMessage();
        _logger.LogWarning("user {user} fail to change the email, error message is: {errorMessage}", _dataService.UserName, errorMessage);
        return BadRequest(errorMessage);
    }
    #endregion

    /// <summary>
    /// 更新用户授权相关缓存
    /// </summary>
    /// <returns></returns>
    private async Task RefreshUserInfo(User user)
    {
        //更新用户授权相关缓存
        await _authorizationService.GetUserContextAsync(user.UserName, true);

        //强制推送客户端更新
        await _communicator.SendAsync(Constants.CACHE_USER_INFO, user.UserName, MessageType.Cache);
    }
}